package com.yifeng.service.impl;

import com.yifeng.model.FileInfo;
import com.yifeng.service.FileStorageService;
import com.yifeng.util.AliOssUtil;
import com.yifeng.util.FileUtil;

import javax.servlet.http.Part;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;

/**
 * 文件存储服务实现类
 */
public class FileStorageServiceImpl implements FileStorageService {

    private static final String UPLOAD_DIR = "uploads";
    private String basePath;

    /*
    负责人：

    功能：默认构造函数，初始化文件存储服务，使用系统默认的上传基础路径，并创建上传目录

    参数：无

    返回值：无
    */
    public FileStorageServiceImpl() {
        this.basePath = FileUtil.getUploadBasePath();
        initStorageDirectory();
    }

    /*
    负责人：

    功能：自定义构造函数，允许外部传入指定的基础存储路径，并初始化上传目录

    参数：customBasePath - 自定义的文件存储根目录路径（绝对路径）

    返回值：无
    */
    public FileStorageServiceImpl(String customBasePath) {
        this.basePath = customBasePath;
        initStorageDirectory();
    }

    /*
    负责人：

    功能：将上传的文件（Part）保存到本地存储，并生成唯一的文件名，同时计算文件MD5值，返回完整的文件信息对象

    参数：filePart - 从HTTP请求中获取的文件上传部件（javax.servlet.http.Part）

    返回值：保存成功返回封装了原始文件名、存储文件名、路径、大小、类型、MD5等信息的FileInfo对象；
            若文件为空或保存失败，则抛出异常
    */
    @Override
    public FileInfo saveFile(Part filePart) throws Exception {
        if (filePart == null || filePart.getSize() == 0) {
            throw new IllegalArgumentException("文件不能为空");
        }

        // 获取文件信息
        String originalFileName = FileUtil.getFileName(filePart);//原始文件名称
        String fileExtension = FileUtil.getFileExtension(originalFileName);//文件扩展名
   /*     String storedFileName = FileUtil.generateUniqueFileName(fileExtension);//存储文件名 随机名称确保唯一性*/

        String storedFileName =originalFileName;//存储文件名
        String relativePath = UPLOAD_DIR + "/" + storedFileName;// 相对路径
        String absolutePath = basePath + File.separator + storedFileName;// 绝对路径

        // 计算文件MD5
        String md5 = calculateFileMD5(filePart);

        // 保存文件
        try (InputStream input = filePart.getInputStream()) {
            Files.copy(input, new File(absolutePath).toPath(), StandardCopyOption.REPLACE_EXISTING);
        }

        // 创建文件信息对象
        FileInfo fileInfo = new FileInfo();
        fileInfo.setOriginalFileName(originalFileName);
        fileInfo.setStoredFileName(storedFileName);
        fileInfo.setRelativePath(relativePath);
        fileInfo.setAbsolutePath(absolutePath);
        fileInfo.setFileSize(filePart.getSize());
        fileInfo.setFileType(fileExtension);
        fileInfo.setMd5(md5);
        fileInfo.setMimeType(filePart.getContentType());

        return fileInfo;
    }

    /*
    负责人：

    功能：根据给定的文件路径（相对或绝对）获取对应的File对象，若文件不存在则返回null

    参数：filePath - 文件路径，可以是相对路径（如 "uploads/xxx.jpg"）或绝对路径

    返回值：若文件存在则返回对应的File对象；否则返回null
    */
    @Override
    public File getFile(String filePath) {
        if (filePath == null || filePath.trim().isEmpty()) {
            return null;
        }

        // 如果是相对路径，转换为绝对路径
        String absolutePath;
        if (filePath.startsWith(UPLOAD_DIR + "/")) {
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
            absolutePath = basePath + File.separator + fileName;
        } else {
            absolutePath = filePath;
        }

        File file = new File(absolutePath);
        return file.exists() ? file : null;
    }

    /*
    负责人：

    功能：根据文件路径删除本地存储的文件

    参数：filePath - 要删除的文件路径（相对或绝对）

    返回值：删除成功返回true；文件不存在或删除失败返回false
    */
    @Override
    public boolean deleteFile(String filePath) {
        File file = getFile(filePath);
        if (file != null && file.exists()) {
            return file.delete();
        }
        return false;
    }

    /*
    负责人：

    功能：检查指定路径的文件是否存在

    参数：filePath - 待检查的文件路径（相对或绝对）

    返回值：文件存在返回true；否则返回false
    */
    @Override
    public boolean fileExists(String filePath) {
        File file = getFile(filePath);
        return file != null && file.exists();
    }

    /*
    负责人：

    功能：根据文件路径获取该文件的元信息（如文件名、大小、扩展名等），不包含MD5和MIME类型

    参数：filePath - 文件路径（相对或绝对）

    返回值：文件存在则返回封装了基本信息的FileInfo对象；否则返回null
    */
    @Override
    public FileInfo getFileInfo(String filePath) {
        File file = getFile(filePath);
        if (file == null || !file.exists()) {
            return null;
        }

        FileInfo fileInfo = new FileInfo();
        fileInfo.setOriginalFileName(file.getName());
        fileInfo.setStoredFileName(file.getName());
        fileInfo.setRelativePath(filePath);
        fileInfo.setAbsolutePath(file.getAbsolutePath());
        fileInfo.setFileSize(file.length());
        fileInfo.setFileType(FileUtil.getFileExtension(file.getName()));

        return fileInfo;
    }

    /*
    负责人：

    功能：获取当前文件存储服务所使用的本地基础存储路径

    参数：无

    返回值：返回文件上传的基础目录绝对路径（字符串）
    */
    @Override
    public String getBasePath() {
        return basePath;
    }

    /*
    负责人：

    功能：计算上传文件内容的MD5摘要值，用于文件去重或完整性校验

    参数：filePart - 上传的文件部件（Part）

    返回值：成功计算返回32位小写MD5字符串；计算过程中出错则返回null
    */
    private String calculateFileMD5(Part filePart) {
        try (InputStream input = filePart.getInputStream()) {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int read;
            while ((read = input.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }

            byte[] md5Bytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : md5Bytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();

        } catch (Exception e) {
            // MD5计算失败不影响主要功能
            return null;
        }
    }

    /*
    负责人：

    功能：初始化文件上传所需的本地存储目录，若目录不存在则创建，并验证目录是否可写

    参数：无

    返回值：无；若目录无法创建或不可写，则抛出RuntimeException
    */
    private void initStorageDirectory() {
        File uploadDir = new File(basePath);
        if (!uploadDir.exists()) {
            boolean created = uploadDir.mkdirs();
            if (!created) {
                throw new RuntimeException("无法创建文件上传目录: " + basePath);
            }
        }

        // 检查目录是否可写
        if (!uploadDir.canWrite()) {
            throw new RuntimeException("文件上传目录不可写: " + basePath);
        }
    }

    /*
    负责人：

    功能：清理上传目录中超过24小时的临时文件，用于释放磁盘空间（可选维护功能）

    参数：无

    返回值：无；直接删除符合条件的文件，不返回操作结果
    */
    public void cleanupTempFiles() {
        File uploadDir = new File(basePath);
        File[] files = uploadDir.listFiles();
        if (files != null) {
            long currentTime = System.currentTimeMillis();
            // 删除超过24小时的临时文件
            for (File file : files) {
                if (file.isFile() && (currentTime - file.lastModified()) > 24 * 60 * 60 * 1000) {
                    file.delete();
                }
            }
        }
    }
}